#ifndef __SIMAPI_H__
#define __SIMAPI_H__

#ifndef SIMULATOR
#define SIMULATOR
#endif

#include <signal.h>

#include "base/simtypes.h"
#include "base/isa_ext.h"

// bring in ISA_EXT_* implementation from target specific directory
#include "isa_ext_impl.h"

// bring in arch specific sim api implementation (i.e. Sim_CleanupStack)
#include "simapiimpl.h"


// some signal definitions
typedef void (*SIGNAL_ACTION)(int, siginfo_t *, void *);
#define SIM_NSIG 4
#define SIM_SIGSTART NSIG
// Fired when a transaction is aborting. 
// note that there is a real SIGABRT so I chose a different name on purpose
#define SIM_SIGROLLBACK (SIM_SIGSTART + 0)
// Fired when a software handler should traverse an undo log to abort
#define SIM_SIGLOGAPPLY (SIM_SIGSTART + 1)
// Fired when a watchpoint is accessed.
#define SIM_SIGWATCH (SIM_SIGSTART + 2)
// Fired when a SOW(snapshot-on-write) needs to be handled.
#define SIM_SIGSOW (SIM_SIGSTART + 3)
// Used for bounds checking
#define MAX_SIG_NUM NSIG + SIM_NSIG

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

// declaration of mainX to ensure C++ apps do not use mangled name
void mainX(int,const char**,const char**);

//------------------------------------------------------------------------
// Basic CPU ID and Count access
//------------------------------------------------------------------------
DECL_ISA_EXT_0_ARG_RET(Sim_GetMyId,    ISA_EXT_GETMYID);
DECL_ISA_EXT_0_ARG_RET(Sim_GetNumCpus, ISA_EXT_GETNUMCPUS);
#define MAX_NUM_CPUS 256

//------------------------------------------------------------------------
// Mode information
//------------------------------------------------------------------------
enum SIM_MODE {
    MODE_UNKNOWN,
    MODE_LOCK,
    MODE_TM
};
DECL_ISA_EXT_0_ARG_RET(Sim_GetMode,    ISA_EXT_MODE);

//------------------------------------------------------------------------
// Simulator Printing
// low overhead printing for debugging that does not perform a system call
//------------------------------------------------------------------------
DECL_ISA_EXT_1_ARG(Sim_Print_fmt,     ISA_EXT_PRINT_FMT,     char const*);
DECL_ISA_EXT_2_ARG(Sim_Print_argv,    ISA_EXT_PRINT_ARGV,    int, int);
DECL_ISA_EXT_1_ARG(Sim_Print_argc,    ISA_EXT_PRINT_ARGC,    int);
DECL_ISA_EXT_1_ARG(Sim_Print_warning, ISA_EXT_PRINT_WARNING, int);
DECL_ISA_EXT_1_ARG(Sim_Print_buffer,  ISA_EXT_PRINT_BUFFER,  Byte);

__INLINE__ void Sim_Print0 (char const* fmt) {
    Sim_Print_fmt(fmt);
    Sim_Print_argc(0);
}
__INLINE__ void Sim_Print1 (char const* fmt, int a1) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argc(1);
}
__INLINE__ void Sim_Print2 (char const* fmt, int a1, int a2) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argc(2);
}
__INLINE__ void Sim_Print3 (char const* fmt, int a1, int a2, int a3) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argv(a3, 2);
    Sim_Print_argc(3);
}
__INLINE__ void Sim_Print4 (char const* fmt, int a1, int a2, int a3, int a4) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argv(a3, 2);
    Sim_Print_argv(a4, 3);
    Sim_Print_argc(4);
}
__INLINE__ void Sim_Print5 (char const* fmt, int a1, int a2, int a3, int a4, int a5) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argv(a3, 2);
    Sim_Print_argv(a4, 3);
    Sim_Print_argv(a5, 4);
    Sim_Print_argc(5);
}
__INLINE__ void Sim_Print6 (char const* fmt, int a1, int a2, int a3, int a4, int a5, int a6) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argv(a3, 2);
    Sim_Print_argv(a4, 3);
    Sim_Print_argv(a5, 4);
    Sim_Print_argv(a6, 5);
    Sim_Print_argc(6);
}
__INLINE__ void Sim_Print7 (char const* fmt, int a1, int a2, int a3, int a4, int a5, int a6, int a7) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argv(a3, 2);
    Sim_Print_argv(a4, 3);
    Sim_Print_argv(a5, 4);
    Sim_Print_argv(a6, 5);
    Sim_Print_argv(a7, 6);
    Sim_Print_argc(7);
}
__INLINE__ void Sim_Print8 (char const* fmt, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argv(a3, 2);
    Sim_Print_argv(a4, 3);
    Sim_Print_argv(a5, 4);
    Sim_Print_argv(a6, 5);
    Sim_Print_argv(a7, 6);
    Sim_Print_argv(a8, 7);
    Sim_Print_argc(8);
}

__INLINE__ void Sim_PrintDouble (char const* fmt, double d) {
    long long  ll = double2longlong(d);
    int a1        = ll >> 32;
    int a2        = ll &  0xFFFFFFFFULL;
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a2, 1);
    Sim_Print_argc(2);
}
__INLINE__ void Sim_Print1Int3Double (char const* fmt, int a1, double d1, double d2, double d3) {
    long long  ll1 = double2longlong(d1);
    int a11        = ll1 >> 32;
    int a12        = ll1 &  0xFFFFFFFFULL;
    long long  ll2 = double2longlong(d2);
    int a21        = ll2 >> 32;
    int a22        = ll2 &  0xFFFFFFFFULL;
    long long  ll3 = double2longlong(d3);
    int a31        = ll3 >> 32;
    int a32        = ll3 &  0xFFFFFFFFULL;
    Sim_Print_fmt(fmt);
    Sim_Print_argv(a1, 0);
    Sim_Print_argv(a11, 1);
    Sim_Print_argv(a12, 2);
    Sim_Print_argv(a21, 3);
    Sim_Print_argv(a22, 4);
    Sim_Print_argv(a31, 5);
    Sim_Print_argv(a32, 6);
    Sim_Print_argc(7);
}

void Sim_Print (char const*, ...) __attribute__ ((format (printf, 1, 2)));

//------------------------------------------------------------------------
// Fast forwarding support
// allows switching from simulation to "real" mode and back again
//------------------------------------------------------------------------

// access to inSimulation flag maintained by sys_goto_real and goto_less
extern bool inSimulation;

void goto_real (); // defined in a ppc/syshandler.s and x86/native/linux/native.cpp
void goto_sim ();  // defined in ppc/CPU.cpp and x86/CPU.cpp

//------------------------------------------------------------------------
// Allow Java programs to disable many error check asserts
// that are only true for applications that exit when with only one
// thread running as opposed to many as with Java.
//------------------------------------------------------------------------

void Sim_DisableShutdownAsserts();

//------------------------------------------------------------------------
// Statistics markers
//------------------------------------------------------------------------

// turn on and off stats collection
DECL_ISA_EXT_0_ARG(Sim_BeginStats, ISA_EXT_STATS_ENABLE);
DECL_ISA_EXT_0_ARG(Sim_EndStats,   ISA_EXT_STATS_DISABLE);

// mark lock cycles
DECL_ISA_EXT_0_ARG(Sim_BeginLock, ISA_EXT_STAT_LOCK);
DECL_ISA_EXT_0_ARG(Sim_EndLock,   ISA_EXT_STAT_UNLOCK);

// mark barrier cycles
DECL_ISA_EXT_0_ARG(Sim_BeginBarrier, ISA_EXT_STAT_BAR);
DECL_ISA_EXT_0_ARG(Sim_EndBarrier,   ISA_EXT_STAT_BAREND);

// turn logging on and off
DECL_ISA_EXT_1_ARG(Sim_SetPrintInstructions, ISA_EXT_SETPRINT, bool);

// allow a processor to be excluded from watchdog detection
DECL_ISA_EXT_1_ARG(Sim_SetIgnoreProgress, ISA_EXT_SET_IGNORE_PROGRESS, bool);

//------------------------------------------------------------------------
// Similar to old TCC_ULoop
//------------------------------------------------------------------------

void Sim_ParallelFork (void *(*run)(void*), void* arg);

//------------------------------------------------------------------------
// setjmp/longjmp
//------------------------------------------------------------------------

DECL_ISA_EXT_0_ARG_RET(Sim_SetJmp, ISA_EXT_SETJMP);
DECL_ISA_EXT_1_ARG(Sim_LongJmp, ISA_EXT_LONGJMP, int);

//------------------------------------------------------------------------
// STM profiling
//------------------------------------------------------------------------
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aAbort,      ISA_EXT_PROF_STM_aABORT );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zAbort,      ISA_EXT_PROF_STM_zABORT );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aCommit,     ISA_EXT_PROF_STM_aCOMMIT );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zCommit,     ISA_EXT_PROF_STM_zCOMMIT );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aNewThread,  ISA_EXT_PROF_STM_aNEWTHREAD );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zNewThread,  ISA_EXT_PROF_STM_zNEWTHREAD );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aRead,       ISA_EXT_PROF_STM_aREAD );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zRead,       ISA_EXT_PROF_STM_zREAD );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aStart,      ISA_EXT_PROF_STM_aSTART );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zStart,      ISA_EXT_PROF_STM_zSTART );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aSterilize,  ISA_EXT_PROF_STM_aSTERILIZE );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zSterilize,  ISA_EXT_PROF_STM_zSTERILIZE );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aWrite,      ISA_EXT_PROF_STM_aWRITE );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zWrite,      ISA_EXT_PROF_STM_zWRITE );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_zWriteLocal, ISA_EXT_PROF_STM_zWRITELOCAL );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_aWriteLocal, ISA_EXT_PROF_STM_aWRITELOCAL );
DECL_ISA_EXT_0_ARG( Sim_Prof_STM_Success,     ISA_EXT_PROF_STM_SUCCESS );


/////////////////////////////////////////////////////

DECL_ISA_EXT_1_CONST(TCC_SetBarrier,   ISA_EXT_BARRIER_NOFLUSH, 1);
DECL_ISA_EXT_1_CONST(TCC_ClearBarrier, ISA_EXT_BARRIER_NOFLUSH, 0);

#ifdef __cplusplus
};
#endif // __cplusplus

#endif // __SIMAPI_H__
